package zdream.control.world.buff;

import zdream.control.region.Terrains;
import zdream.control.world.*;

/**
 * Foe 如果 box 至少一半在水中
 * 则判定 Foe 在该状态中.
 *
 * <p>判定是否在水里可能不会太严格, 其实很水的, 就看角色 box
 * 的中点在不在水里 (是不是很懒啊)
 * <p>本游戏中的水有两类, 一种是 tiled 水, 一种是 field 水.
 * 两种水是一个效果.
 * <p>如果 Foe 在水中, box 的 gravityScale 降为原先的
 * {@link #GRAVITY_SCALE_IN_WATER} 倍. (洛克人能跳 9 格)
 * 离开水之后恢复.
 * </p>
 *
 * <p>原类名: SinkInWaterState</p>
 *
 * @author Zdream
 * @date 2022-06-23
 * @since 0.0.1
 */
public class SinkInWaterBuff extends MFoeBuff {
	public static final String CATEGORY = "sinkInWater";
	public static final float GRAVITY_SCALE_IN_WATER = 0.358f;

	public SinkInWaterBuff(MFoe foe) {
		super(foe);
		startTime = foe.world.ticker.activeTime;
		addTag(MFoeConstants.CATEGORY_GRAVITY_ASSOCIATED);
	}

	@Override
	public void init() {
		onHook(MHookType.TICK)
				.withPriority(-200)
				.handle(this::check)
				.buildAndSave();
		onHook(MHookType.REFRESH_GRAVITY)
				.withPriority(-10)
				.handle(this::updateGravity)
				.buildAndSave();
	}

	@Override
	public String getCategory() {
		return CATEGORY;
	}

	/**
	 * 入水时的时间, foeActiveTime
	 */
	public final long startTime;

	private void check() {
		boolean sunk = inWater(foe);

		// TODO 这里暂时不考虑 field 水

		if (!sunk) {
			foe.removeBuff(this);
		}
	}

	private void updateGravity(MHooks.MHookContext rawContext) {
		final MHooks.GravityRefreshContext context = rawContext.as();
		context.scale *= GRAVITY_SCALE_IN_WATER;
	}

	public static boolean inWater(MFoe foe) {
		// 判断 tiled 水
		float cx = foe.getCenterX(), cy = foe.getCenterY();
		int icx = MFoe.pToBlock(cx), icy = MFoe.pToBlock(cy);
		byte filler = foe.getTerrainHandler().getWorldFiller(icx, icy);
		return Terrains.hasWater(filler);
	}
}
